﻿using System;
using System.Collections.Generic;
using System.Data.Common;
using System.Linq;
using System.Reflection;
using System.Text;

namespace BlueCMS.Core.Data
{
    public class DataHelpers
    {
        private static T CreateObject<T>(DbDataReader reader)
        {
            T newObject = Activator.CreateInstance<T>();

            HydrateObject(newObject, reader);

            return newObject;
        }

        private static int[] GetOrdinals(PropertyInfo[] properties, DbDataReader reader)
        {
            int[] ordinals = new int[properties.Length];

            if (reader != null)
            {
                Dictionary<string, int> columns = new Dictionary<string, int>();
                for (int i = 0; i < reader.FieldCount; i++)
                {
                    columns.Add(reader.GetName(i).ToUpper(), i);
                }

                for (int i = 0; i < properties.Length; i++)
                {
                    string propertyName = properties[i].Name.ToUpper();
                    if (columns.ContainsKey(propertyName))
                    {
                        ordinals[i] = columns[propertyName];
                    }
                    else
                    {
                        ordinals[i] = -1;
                    }
                }
            }

            return ordinals;
        }

        private static PropertyInfo[] GetPropertyInfo(Type type)
        {
            // TODO: Cache this
            return type.GetProperties();
        }

        private static void HydrateObject(object destination, DbDataReader reader)
        {
            PropertyInfo[] properties = GetPropertyInfo(destination.GetType());
            int[] ordinals = GetOrdinals(properties, reader);

            for(int i = 0; i < properties.Length; i++)
            {
                var propertyInfo = properties[i];
                var propertyType = propertyInfo.PropertyType;

                if(propertyInfo.CanWrite)
                {
                    if(ordinals[i] > -1)
                    {
                        var dataValue = reader.GetValue(ordinals[i]);
                        var dataType = dataValue.GetType();

                        if(reader.IsDBNull(ordinals[i]))
                        {
                            propertyInfo.SetValue(destination, null, null);
                        }
                        else if (propertyType.Equals(dataType))
                        {
                            propertyInfo.SetValue(destination, dataValue, null);
                        }
                        else
                        {
                            try
                            {
                                if (propertyType.BaseType.Equals(typeof(System.Enum)))
                                {
                                    propertyInfo.SetValue(destination, System.Enum.ToObject(propertyType, dataValue), null);
                                }
                                else if (propertyType.FullName.Equals("System.Guid"))
                                {
                                    propertyInfo.SetValue(destination, Convert.ChangeType(new Guid(dataValue.ToString()), propertyType), null);
                                }
                                else
                                {
                                    propertyInfo.SetValue(destination, dataValue, null);
                                }
                            }
                            catch
                            {
                                propertyInfo.SetValue(destination, Convert.ChangeType(dataValue, propertyType), null);
                            }
                        }
                    }
                }
            }
        }

        public static IList<T> FillCollection<T>(DbDataReader reader)
        {
            return FillCollection<T>(reader, true);
        }

        public static IList<T> FillCollection<T>(DbDataReader reader, bool manageDataReader)
        {
            List<T> ret = new List<T>();

            if (reader != null)
            {
                while (reader.Read())
                {
                    T newObject = FillObject<T>(reader, false);
                    ret.Add(newObject);
                }

                if (manageDataReader)
                {
                    reader.Close();
                }
            }

            return ret;
        }

        public static T FillObject<T>(DbDataReader reader)
        {
            return FillObject<T>(reader, true);
        }

        public static T FillObject<T>(DbDataReader reader, bool manageDataReader)
        {
            if (reader == null)
            {
                return default(T);
            }

            T newObject;

            bool canRead = true;
            if (manageDataReader)
            {
                if (!reader.Read())
                {
                    canRead = false;
                }
            }

            if (canRead)
            {
                newObject = CreateObject<T>(reader);
            }
            else
            {
                newObject = default(T);
            }

            if (manageDataReader)
            {
                reader.Close();
            }

            return newObject;
        }
    }
}
